/*
This is the main file accompanying the online bipartite matching library.
This was used to generate experiments described in the paper:

Allan Borodin, Christodoulos Karavasilis, Denis Pankratov. "An Experimental
Study of Algorithms for Online Bipartite Matching."

To compile this file you need to have installed and configured GNU linear
programming kit. See <http://www.gnu.org/software/glpk/glpk.html>

Copyright (C) 2018  Denis Pankratov

Modifications Copyright (C) 2019 Christodoulos Karavasilis

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <https://www.gnu.org/licenses/>.
*/

#include "stdafx.h"
#include "graph.h" // the main file with all matching algos
#include <cstdlib>
#include "randomgraphgen.h" // for generating random instances
#include <ctime>
#include <cstdio>
#include <fstream>
//#include "tests.h" // for unit testing
#include "bmcommon.h" // some global functions and global variables
#include <iomanip>

/* Returns the matching vector for offline side. */
vector<int> getOffline(Graph &g, vector<int> &online) {
	int off = g.getNumVertices() - (int)online.size();
	vector<int> res(off, -1);
	for (int i = 0; i < (int)online.size(); i++) {
		if (online[i] != -1) {
			res[online[i] - g.getLeftSize()] = i;
		}
	}
	return res;
}

/* Experiment that times how much greedy start improves. */
void experimentTimeGreedyStart(int n, double c) {
	clock_t t;
	Graph g = bipartiteErdosRenyi(n, n, c / n);

	printf("Graph has %d nodes %d edges\n", g.getNumVertices(), 
		g.getNumEdges());

	t = clock();
	vector<int> opt1 = g.greedy();
	g.maxMatching(opt1);
	t = clock() - t;
	printf("Greedy+opt took %d clicks (%f seconds), OPT = %d\n", t, 
		((double)t) / CLOCKS_PER_SEC, Graph::countMatchingSize(opt1));

	t = clock();
	vector<int> greedy = g.greedy();
	vector<int> opt2 = g.secondPass(greedy);
	g.maxMatching(opt2);
	t = clock() - t;
	printf("Greedy+2ndPass+opt took %d clicks (%f seconds), OPT = %d\n", t, 
		((double)t) / CLOCKS_PER_SEC, Graph::countMatchingSize(opt2));

	t = clock();
	vector<int> opt3(g.getLeftSize(), -1);
	g.maxMatching(opt3);
	t = clock() - t;
	printf("Empty+opt took %d clicks (%f seconds), OPT = %d\n", t, 
		((double)t) / CLOCKS_PER_SEC, Graph::countMatchingSize(opt3));
}

/* 
Runs all algorithms on a known i.i.d. sampled from a given type graph.

INPUT(S):
  g -- a given type graph
OUTPUT(S):
  res -- res[i] stores the size of the matching returned by algorithm i
  times -- times[i] stores the runtime of algorithm i
  names -- names[i] stores the name of algorithm i
CONDITION(S):
  OPT must always be recorded last
*/
void runIIDAllOnGraph(Graph &g, vector<double> & res, vector<double> & times, 
	vector<string> & names) {
	res.clear();
	names.clear();
	times.clear();

	clock_t t;
	int n = g.getLeftSize();
	vector<int> red, blue; // for Feldman
	vector<int> bahmanired, bahmaniblue; // for Bahmani
	vector<int> types = iid(n, n); // for the actual experiment
	Graph instance = g.createInstanceGraph(types); // for greedy algos
	int fracOptRep = 100; // num of reps for estimating fractional opt

	// Variables to store returned matchings
	vector<int> feldman, feldmanG;
	vector<int> manshadi, manshadiG;
	vector<int> bahmani, bahmaniG;
	vector<int> jaillet, jailletG;
	vector<int> brubach, brubachG;
	vector<int> minDegree, karpSipser, greedy, secondPass, thirdPass, randomRanking, opt;

	// Variables to store time of execution
	double tFeldman, tFeldmanG;
	double tManshadi, tManshadiG;
	double tBahmani, tBahmaniG;
	double tJaillet, tJailletG;
	double tBrubach, tBrubachG;
	double tMinDegree, tKarpSipser, tGreedy, tSecondPass, tThirdPass, tRandomRanking, tOpt;

	// ==========    FELDMAN!!!!!! ===========//

	if (DEBUG_LEVEL > 0)
		cerr << "Getting Feldman advice ..." << endl;
	t = clock();
	g.getFeldmanAdvice(red, blue);
	t = clock() - t;
	tFeldman = tFeldmanG = (double)t / CLOCKS_PER_SEC;

	if (DEBUG_LEVEL > 0)
		cerr << "Running Feldman" << endl;
	t = clock();
	feldman = g.getFeldmanMatching(types, red, blue, false);
	t = clock() - t;
	tFeldman += (double)t / CLOCKS_PER_SEC;

	t = clock();
	feldmanG = g.getFeldmanMatching(types, red, blue, true);
	t = clock() - t;
	tFeldmanG += (double)t / CLOCKS_PER_SEC;

	// ==========    BAHMANI!!!!!! ===========//

	if (DEBUG_LEVEL > 0)
		cerr << "Getting Bahmani advice ..." << endl;
	t = clock();
	g.getBahmaniAdvice(bahmanired, bahmaniblue);
	t = clock() - t;
	tBahmani = tBahmaniG = (double)t / CLOCKS_PER_SEC;

	if (DEBUG_LEVEL > 0)
		cerr << "Running Bahmani" << endl;

	t = clock();
	bahmani = g.getFeldmanMatching(types, bahmanired, bahmaniblue, false);
	t = clock() - t;
	tBahmani += (double)t / CLOCKS_PER_SEC;

	t = clock();
	bahmaniG = g.getFeldmanMatching(types, bahmanired, bahmaniblue, true);
	t = clock() - t;
	tBahmaniG += (double)t / CLOCKS_PER_SEC;

	// ==========    MANSHADI!!!!!! ===========//

	if (DEBUG_LEVEL > 0)
		cerr << "Estimating fractional opt with " << fracOptRep << ""
			" repetitions..." << endl;

	t = clock();
	vector<vector<pair<double, int> > > fracOpt = 
		g.estimateFractionalOPTIIDMC(fracOptRep);
	t = clock() - t;
	tManshadi = tManshadiG = (double)t / CLOCKS_PER_SEC;

	if (DEBUG_LEVEL > 0)
		cerr << "Running Manshadi" << endl;

	t = clock();
	manshadi = g.getManshadiMatching(types, fracOpt, false);
	t = clock() - t;
	tManshadi += (double)t / CLOCKS_PER_SEC;

	t = clock();
	manshadiG = g.getManshadiMatching(types, fracOpt, true);
	t = clock() - t;
	tManshadiG += (double)t / CLOCKS_PER_SEC;

	// ==========    JAILLET!!!!!! ===========//
	if (DEBUG_LEVEL > 0)
		cerr << "Getting jaillet advice" << endl;
	
	t = clock();
	vector<vector<int> > jailletAdvice = g.getJailletAdvice();
	t = clock() - t;
	tJaillet = tJailletG = (double)t / CLOCKS_PER_SEC;

	if (DEBUG_LEVEL > 0)
		cerr << "Running Jaillet" << endl;
	t = clock();
	jaillet = g.getJailletMatching(types, jailletAdvice, false);
	t = clock() - t;
	tJaillet += (double)t / CLOCKS_PER_SEC;

	t = clock();
	jailletG = g.getJailletMatching(types, jailletAdvice, true);
	t = clock() - t;
	tJailletG += (double)t / CLOCKS_PER_SEC;

	// ==========    BRUBACH!!!!!! ===========//

	if (DEBUG_LEVEL > 0)
		cerr << "Getting brubach advice" << endl;

	t = clock();
	vector<vector<pair<double, int> > > brubachAdvice = g.getBrubachAdvice();
	t = clock() - t;
	tBrubach = tBrubachG = (double)t / CLOCKS_PER_SEC;
	
	if (DEBUG_LEVEL > 0)
		cerr << "Running Brubach" << endl;

	t = clock();
	brubach = g.getBrubachMatching(types, brubachAdvice, false);
	t = clock() - t;
	tBrubach += (double)t / CLOCKS_PER_SEC;

	t = clock();
	brubachG = g.getBrubachMatching(types, brubachAdvice, true);
	t = clock() - t;
	tBrubachG += (double)t / CLOCKS_PER_SEC;

	// ==========    GREEDY THINGS!!!!!! ===========//
	
	if (DEBUG_LEVEL > 0)
		cerr << "Running online mindegree" << endl;

	t = clock();
	minDegree = instance.minDegree();
	t = clock() - t;
	tMinDegree = (double)t / CLOCKS_PER_SEC;

	if (DEBUG_LEVEL > 0)
		cerr << "Running online Karp-Sipser" << endl;

	t = clock();
	karpSipser = instance.karp_sipser();
	t = clock() - t;
	tKarpSipser = (double)t / CLOCKS_PER_SEC;

	if (DEBUG_LEVEL > 0)
		cerr << "Running greedy" << endl;
	
	t = clock();
	greedy = instance.greedy();
	t = clock() - t;
	tGreedy = tSecondPass = (double)t / CLOCKS_PER_SEC;

	if (DEBUG_LEVEL > 0)
		cerr << "Running second pass" << endl;

	t = clock();
	secondPass = instance.secondPass(greedy);
	t = clock() - t;
	tSecondPass += (double)t / CLOCKS_PER_SEC;

	if (DEBUG_LEVEL > 0)
		cerr << "Running third pass" << endl;

	t = clock();
	thirdPass = instance.thirdPass(greedy, secondPass);
	t = clock() - t;
	tThirdPass = tSecondPass + (double)t / CLOCKS_PER_SEC;
	
	if (DEBUG_LEVEL > 0)
		cerr << "Running Ranking" << endl;

	t = clock();
	randomRanking = instance.randomRanking();
	t = clock() - t;
	tRandomRanking = (double)t / CLOCKS_PER_SEC;


	// ==========    OPT!!!!!! ===========//
	if (DEBUG_LEVEL > 0)
		cerr << "Running OPT on Greedy" << endl;
	
	opt = greedy;
	t = clock();
	instance.maxMatching(opt);
	t = clock() - t;
	tOpt = tGreedy + (double)t / CLOCKS_PER_SEC;
	
	bool diff = false;
	for (int i = 0; i < secondPass.size(); i++) {
		if (secondPass[i] != thirdPass[i]) {
			diff = true;
		}
	}
	
	res.push_back(Graph::countMatchingSize(feldman)); 
	times.push_back(tFeldman); names.push_back("FeldmanEtAl");
	res.push_back(Graph::countMatchingSize(feldmanG)); 
	times.push_back(tFeldmanG); names.push_back("FeldmanEtAl(g)");
	res.push_back(Graph::countMatchingSize(bahmani)); 
	times.push_back(tBahmani); names.push_back("BahmaniKapralov");
	res.push_back(Graph::countMatchingSize(bahmaniG)); 
	times.push_back(tBahmaniG); names.push_back("BahmaniKapralov(g)");
	res.push_back(Graph::countMatchingSize(manshadi)); 
	times.push_back(tManshadi); names.push_back("ManshadiEtAl");
	res.push_back(Graph::countMatchingSize(manshadiG)); 
	times.push_back(tManshadiG); names.push_back("ManshadiEtAl(g)");
	res.push_back(Graph::countMatchingSize(jaillet)); 
	times.push_back(tJaillet); names.push_back("JailletLu");
	res.push_back(Graph::countMatchingSize(jailletG)); 
	times.push_back(tJailletG); names.push_back("JailletLu(g)");
	res.push_back(Graph::countMatchingSize(brubach)); 
	times.push_back(tBrubach); names.push_back("BrubachEtAl");
	res.push_back(Graph::countMatchingSize(brubachG)); 
	times.push_back(tBrubachG); names.push_back("BrubachEtAl(g)");
	res.push_back(Graph::countMatchingSize(minDegree));
	times.push_back(tMinDegree); names.push_back("MinDegree");
	res.push_back(Graph::countMatchingSize(karpSipser));
	times.push_back(tKarpSipser); names.push_back("KarpSipser");
	res.push_back(Graph::countMatchingSize(greedy)); 
	times.push_back(tGreedy); names.push_back("SimpleGreedy");
	res.push_back(Graph::countMatchingSize(randomRanking)); 
	times.push_back(tRandomRanking); names.push_back("Ranking");
	res.push_back(Graph::countMatchingSize(secondPass)); 
	times.push_back(tSecondPass); names.push_back("Category-Advice");
	res.push_back(Graph::countMatchingSize(thirdPass)); 
	times.push_back(tThirdPass); names.push_back("3-Pass");
	res.push_back(Graph::countMatchingSize(opt)); 
	times.push_back(tOpt); names.push_back("OPT"); // OPT MUST ALWAYS BE LAST!	
}

/*
Runs the Erdos-Renyi type graph experiment:

G_{n,n,c/n} is generated rep times for each value of c from cfrom to cto with
step cstep. Records mean competitive ratios and mean runtimes together with 
sample standard deviations in 4 separate files.
*/
void experimentErdosRenyi() {
	int n = 1000;
	int rep = 100;
	double cfrom = 0.1;
	double cto = 15.0;
	double cstep = 0.2;
	bool competitive = true;
	
	string base = "ErdosRenyi\\";
	string filename = base+"ErdosRenyi";
	ofstream fileResMean, fileResStd, fileTimesMean, fileTimesStd;

	cerr << "RUNNING ERDOS-RENYI EXPERIMENT" << endl;
	cerr << "  Type graph is G_{n,n,c/n}, where" << endl;
	cerr << "    n = " << n << endl;
	cerr << "    c ranges from " << cfrom << " to " << cto << ""
		" with a step " << cstep << endl;
	cerr << "    each value of c is repeated " << rep << " time(s)." << endl;
	cerr << "  Results are recorded in the files " << filename << ""
		"(ResMean.txt,ResStd.txt,TimesMean.txt,TimesStd.txt)" << endl;

	string fnameResMean = filename + "ResMean.txt";
	string fnameResStd = filename + "ResStd.txt";
	string fnameTimesMean = filename + "TimesMean.txt";
	string fnameTimesStd = filename + "TimesStd.txt";

	fileResMean.open(fnameResMean);
	fileResStd.open(fnameResStd);
	fileTimesMean.open(fnameTimesMean);
	fileTimesStd.open(fnameTimesStd);

	bool firstC = true;
	for (double c = cfrom; c < cto + FLT_EPSILON; c += cstep) {
		cerr << "Current c: " << c << endl;
		vector<vector<double> > res;
		vector<vector<double> > times;
		vector<string> names;
		
		cerr << "Rep no";
		for (int z = 0; z < rep; z++) {
			cerr << " " << (z + 1);

			vector<double> curRes;
			vector<double> curTimes;

			Graph g = bipartiteErdosRenyi(n, n, c / n);
			runIIDAllOnGraph(g, curRes, curTimes, names);

			if (z == 0) {
				for (int i = 0; i < names.size(); i++) {
					res.push_back(vector<double>());
					times.push_back(vector<double>());
				}
			}
			for (int i = 0; i < names.size(); i++) {
				res[i].push_back(curRes[i]);
				times[i].push_back(curTimes[i]);
			}
		}
		cerr << endl;

		// compute statistics
		vector<double> resMean(names.size(), 0.0);
		vector<double> resStd(names.size(), 0.0);
		vector<double> timesMean(names.size(), 0.0);
		vector<double> timesStd(names.size(),0.0);

		for (int i = 0; i < (int)names.size(); i++) {
			getMeanAndStd(res[i], resMean[i], resStd[i]);
			getMeanAndStd(times[i], timesMean[i], timesStd[i]);
		}

		// WRITE HEADER
		if (firstC) {
			fileResMean << "c";
			fileResStd << "c";
			fileTimesMean << "c";
			fileTimesStd << "c";

			for (int i = 0; i < (int)names.size(); i++) {
				fileResMean << " " << names[i];
				fileResStd << " " << names[i];
				fileTimesMean << " " << names[i];
				fileTimesStd << " " << names[i];
			}
			fileResMean << endl;
			fileResStd << endl;
			fileTimesMean << endl;
			fileTimesStd << endl;

			firstC = false;
		}

		fileResMean << c;
		fileResStd << c;
		fileTimesMean << c;
		fileTimesStd << c;
		for (int i = 0; i < (int)names.size(); i++) {
			fileResMean << " ";
			fileResStd << " ";
			fileTimesMean << " ";
			fileTimesStd << " ";

			if (competitive) {
				fileResMean << (resMean[i] / resMean[(int)resMean.size() - 1]);
				fileResStd << (resStd[i] / resMean[(int)resMean.size() - 1]);
			}
			else {
				fileResMean << resMean[i] / n;
				fileResStd << resStd[i] / n;
			}
			fileTimesMean << timesMean[i];
			fileTimesStd << timesStd[i];
		}
		fileResMean << endl;
		fileResStd << endl;
		fileTimesMean << endl;
		fileTimesStd << endl;
	}

	fileResMean.close();
	fileResStd.close();
	fileTimesMean.close();
	fileTimesStd.close();

	cerr << "DONE" << endl;
}

/*
Runs the preferentialAttachmentBiGraph type graph experiment:

preferentialAttachmentBiGraph is generated rep times for each value of c from 
cfrom to cto with step cstep. Records mean competitive ratios and mean runtimes
together with sample standard deviations in 4 separate files.
*/
void experimentPreferentialAttachmentBiGraph() {
	int n = 1000;
	int rep = 100;
	double cfrom = 10.7;
	double cto = 15.0;
	double cstep = 0.2;
	bool competitive = true;

	string base = "PreferentialAttachment\\";
	string filename = base+ "PreferentialAttachment";
	ofstream fileResMean, fileResStd, fileTimesMean, fileTimesStd;

	cerr << "RUNNING preferentialAttachmentBiGraph EXPERIMENT" << endl;
	cerr << "  Type graph is preferentialAttachmentBiGraph with parameter c, "
		"where" << endl;
	cerr << "    n = " << n << endl;
	cerr << "    c ranges from " << cfrom << " to " << cto << ""
		" with a step " << cstep << endl;
	cerr << "    each value of c is repeated " << rep << " time(s)." << endl;
	cerr << "  Results are recorded in the files " << filename << ""
		"(ResMean.txt,ResStd.txt,TimesMean.txt,TimesStd.txt)" << endl;

	string fnameResMean = filename + "ResMean.txt";
	string fnameResStd = filename + "ResStd.txt";
	string fnameTimesMean = filename + "TimesMean.txt";
	string fnameTimesStd = filename + "TimesStd.txt";

	fileResMean.open(fnameResMean);
	fileResStd.open(fnameResStd);
	fileTimesMean.open(fnameTimesMean);
	fileTimesStd.open(fnameTimesStd);

	bool firstC = true;
	for (double c = cfrom; c < cto + FLT_EPSILON; c += cstep) {
		cerr << "Current c: " << c << endl;
		vector<vector<double> > res;
		vector<vector<double> > times;
		vector<string> names;

		cerr << "Rep no";
		for (int z = 0; z < rep; z++) {
			cerr << " " << (z + 1);

			vector<double> curRes;
			vector<double> curTimes;

			Graph g = preferentialAttachmentBiGraph(n, c);
			runIIDAllOnGraph(g, curRes, curTimes, names);

			if (z == 0) {
				for (int i = 0; i < names.size(); i++) {
					res.push_back(vector<double>());
					times.push_back(vector<double>());
				}
			}
			for (int i = 0; i < names.size(); i++) {
				res[i].push_back(curRes[i]);
				times[i].push_back(curTimes[i]);
			}
		}
		cerr << endl;

		// compute statistics
		vector<double> resMean(names.size(), 0.0);
		vector<double> resStd(names.size(), 0.0);
		vector<double> timesMean(names.size(), 0.0);
		vector<double> timesStd(names.size(), 0.0);

		for (int i = 0; i < (int)names.size(); i++) {
			getMeanAndStd(res[i], resMean[i], resStd[i]);
			getMeanAndStd(times[i], timesMean[i], timesStd[i]);
		}

		// WRITE HEADER
		if (firstC) {
			fileResMean << "c";
			fileResStd << "c";
			fileTimesMean << "c";
			fileTimesStd << "c";

			for (int i = 0; i < (int)names.size(); i++) {
				fileResMean << " " << names[i];
				fileResStd << " " << names[i];
				fileTimesMean << " " << names[i];
				fileTimesStd << " " << names[i];
			}
			fileResMean << endl;
			fileResStd << endl;
			fileTimesMean << endl;
			fileTimesStd << endl;

			firstC = false;
		}

		fileResMean << c;
		fileResStd << c;
		fileTimesMean << c;
		fileTimesStd << c;
		for (int i = 0; i < (int)names.size(); i++) {
			fileResMean << " ";
			fileResStd << " ";
			fileTimesMean << " ";
			fileTimesStd << " ";

			if (competitive) {
				fileResMean << (resMean[i] / resMean[(int)resMean.size() - 1]);
				fileResStd << (resStd[i] / resMean[(int)resMean.size() - 1]);
			}
			else {
				fileResMean << resMean[i] / n;
				fileResStd << resStd[i] / n;
			}
			fileTimesMean << timesMean[i];
			fileTimesStd << timesStd[i];
		}
		fileResMean << endl;
		fileResStd << endl;
		fileTimesMean << endl;
		fileTimesStd << endl;
	}

	fileResMean.close();
	fileResStd.close();
	fileTimesMean.close();
	fileTimesStd.close();

	cerr << "DONE" << endl;
}

/*
Runs the regular type graph experiment:

G_{n,n,d} is generated rep times for each value of d from dfrom to dto with
step dstep. Records mean competitive ratios and mean runtimes together with 
sample standard deviations in 4 separate files.
*/
void experimentRegular() {
	int n = 1000;
	int rep = 100;
	double dfrom = 1;
	double dto = 30;
	double dstep = 1;
	bool competitive = true;
	bool leftRegular = true;

	string base = "Regular\\";
	string filename = base+string((leftRegular)?"":"Right")+"Regular";
	ofstream fileResMean, fileResStd, fileTimesMean, fileTimesStd;

	cerr << "RUNNING REGULAR EXPERIMENT" << endl;
	cerr << "  Type graph is G_{n,n,d}, where" << endl;
	cerr << "    n = " << n << endl;
	cerr << "    d ranges from " << dfrom << " to " << dto << ""
		" with a step " << dstep << endl;
	cerr << "    each value of c is repeated " << rep << " time(s)." << endl;
	cerr << "  Results are recorded in the files " << filename << ""
		"(ResMean.txt,ResStd.txt,TimesMean.txt,TimesStd.txt)" << endl;

	string fnameResMean = filename + "ResMean.txt";
	string fnameResStd = filename + "ResStd.txt";
	string fnameTimesMean = filename + "TimesMean.txt";
	string fnameTimesStd = filename + "TimesStd.txt";

	fileResMean.open(fnameResMean);
	fileResStd.open(fnameResStd);
	fileTimesMean.open(fnameTimesMean);
	fileTimesStd.open(fnameTimesStd);

	bool firstC = true;
	for (int d = dfrom; d < dto + FLT_EPSILON; d += dstep) {
		cerr << "Current d: " << d << endl;
		vector<vector<double> > res;
		vector<vector<double> > times;
		vector<string> names;

		cerr << "Rep no";
		for (int z = 0; z < rep; z++) {
			cerr << " " << (z + 1);

			vector<double> curRes;
			vector<double> curTimes;

			Graph g = generateDRegular(n, n, d, leftRegular);
			runIIDAllOnGraph(g, curRes, curTimes, names);

			if (z == 0) {
				for (int i = 0; i < names.size(); i++) {
					res.push_back(vector<double>());
					times.push_back(vector<double>());
				}
			}
			for (int i = 0; i < names.size(); i++) {
				res[i].push_back(curRes[i]);
				times[i].push_back(curTimes[i]);
			}
		}
		cerr << endl;

		// compute statistics
		vector<double> resMean(names.size(), 0.0);
		vector<double> resStd(names.size(), 0.0);
		vector<double> timesMean(names.size(), 0.0);
		vector<double> timesStd(names.size(), 0.0);

		for (int i = 0; i < (int)names.size(); i++) {
			getMeanAndStd(res[i], resMean[i], resStd[i]);
			getMeanAndStd(times[i], timesMean[i], timesStd[i]);
		}

		// WRITE HEADER
		if (firstC) {
			fileResMean << "d";
			fileResStd << "d";
			fileTimesMean << "d";
			fileTimesStd << "d";

			for (int i = 0; i < (int)names.size(); i++) {
				fileResMean << " " << names[i];
				fileResStd << " " << names[i];
				fileTimesMean << " " << names[i];
				fileTimesStd << " " << names[i];
			}
			fileResMean << endl;
			fileResStd << endl;
			fileTimesMean << endl;
			fileTimesStd << endl;

			firstC = false;
		}

		fileResMean << d;
		fileResStd << d;
		fileTimesMean << d;
		fileTimesStd << d;
		for (int i = 0; i < (int)names.size(); i++) {
			fileResMean << " ";
			fileResStd << " ";
			fileTimesMean << " ";
			fileTimesStd << " ";

			if (competitive) {
				fileResMean << (resMean[i] / resMean[(int)resMean.size() - 1]);
				fileResStd << (resStd[i] / resMean[(int)resMean.size() - 1]);
			}
			else {
				fileResMean << resMean[i] / n;
				fileResStd << resStd[i] / n;
			}
			fileTimesMean << timesMean[i];
			fileTimesStd << timesStd[i];
		}
		fileResMean << endl;
		fileResStd << endl;
		fileTimesMean << endl;
		fileTimesStd << endl;
	}

	fileResMean.close();
	fileResStd.close();
	fileTimesMean.close();
	fileTimesStd.close();

	cerr << "DONE" << endl;
}

/*
Runs the regular type graph experiment:
G_{n,n,d} is generated rep times for each value of d from dfrom to dto with
step dstep. Records mean competitive ratios and mean runtimes together with
sample standard deviations in 4 separate files.
*/
void experimentPowerLawMolloyReed() {
	int n = 1000;
	int rep = 100;
	// exponent range
	double efrom = 0.5;
	double eto = 4.0;
	double estep = 0.1;
	// cutoff range
	double cfrom = 1.0;
	double cto = 100.0;
	double cstep = 5.0;
	bool competitive = true;
	bool randomBipartition = true;

	string base = "MolloyReed\\";
	string filename = base+"MolloyReed";
	ofstream fileResMean, fileResStd, fileTimesMean, fileTimesStd;

	cerr << "RUNNING REGULAR EXPERIMENT" << endl;
	cerr << "  Type graph is generated by randomly bipartitioning a simple"
		" graph obtained from Molloy Reed" << endl;
	cerr << "  with respect to power law distribution, where" << endl;
	cerr << "    n = " << n << " nodes total" << endl;
	cerr << "    exponent ranges from " << efrom << " to " << eto << ""
		" with a step " << estep << endl;
	cerr << "    cutoff ranges from " << cfrom << " to " << cto << ""
		" with a step " << cstep << endl;
	cerr << "    each value of (exponent, cutoff) is repeated " << rep << ""
		" time(s)." << endl;
	cerr << "  Results are recorded in the files " << filename << ""
		"(ResMean.txt,ResStd.txt,TimesMean.txt,TimesStd.txt)" << endl;

	string fnameResMean = filename + "ResMean.txt";
	string fnameResStd = filename + "ResStd.txt";
	string fnameTimesMean = filename + "TimesMean.txt";
	string fnameTimesStd = filename + "TimesStd.txt";

	fileResMean.open(fnameResMean);
	fileResStd.open(fnameResStd);
	fileTimesMean.open(fnameTimesMean);
	fileTimesStd.open(fnameTimesStd);

	bool firstC = true;
	for (double exponent = efrom; exponent < eto + FLT_EPSILON; 
		exponent += estep) {
		for (double cutoff = cfrom; cutoff < cto + FLT_EPSILON; 
			cutoff += cstep) {
			cerr << "Current exponent: " << exponent << endl;
			cerr << "Current cutoff: " << cutoff << endl;
			vector<vector<double> > res;
			vector<vector<double> > times;
			vector<string> names;

			cerr << "Rep no";
			for (int z = 0; z < rep; z++) {
				cerr << " " << (z + 1);

				vector<double> curRes;
				vector<double> curTimes;

				vector<double> plCDF = powerLawCDF(exponent, cutoff);
				Graph g = generateMolloyReed(n, plCDF, randomBipartition);
				runIIDAllOnGraph(g, curRes, curTimes, names);

				if (z == 0) {
					for (int i = 0; i < names.size(); i++) {
						res.push_back(vector<double>());
						times.push_back(vector<double>());
					}
				}
				for (int i = 0; i < names.size(); i++) {
					res[i].push_back(curRes[i]);
					times[i].push_back(curTimes[i]);
				}
			}
			cerr << endl;

			// compute statistics
			vector<double> resMean(names.size(), 0.0);
			vector<double> resStd(names.size(), 0.0);
			vector<double> timesMean(names.size(), 0.0);
			vector<double> timesStd(names.size(), 0.0);

			for (int i = 0; i < (int)names.size(); i++) {
				getMeanAndStd(res[i], resMean[i], resStd[i]);
				getMeanAndStd(times[i], timesMean[i], timesStd[i]);
			}

			// WRITE HEADER
			if (firstC) {
				fileResMean << "exponent ";
				fileResStd << "exponent ";
				fileTimesMean << "expnoent ";
				fileTimesStd << "exponent ";


				fileResMean << "cutoff";
				fileResStd << "cutoff";
				fileTimesMean << "cutoff";
				fileTimesStd << "cutoff";

				for (int i = 0; i < (int)names.size(); i++) {
					fileResMean << " " << names[i];
					fileResStd << " " << names[i];
					fileTimesMean << " " << names[i];
					fileTimesStd << " " << names[i];
				}
				fileResMean << endl;
				fileResStd << endl;
				fileTimesMean << endl;
				fileTimesStd << endl;

				firstC = false;
			}

			fileResMean << exponent << " " << cutoff;
			fileResStd << exponent << " " << cutoff;
			fileTimesMean << exponent << " " << cutoff;
			fileTimesStd << exponent << " " << cutoff;

			for (int i = 0; i < (int)names.size(); i++) {
				fileResMean << " ";
				fileResStd << " ";
				fileTimesMean << " ";
				fileTimesStd << " ";

				if (competitive) {
					fileResMean << 
						(resMean[i] / resMean[(int)resMean.size() - 1]);
					fileResStd << 
						(resStd[i] / resMean[(int)resMean.size() - 1]);
				}
				else {
					fileResMean << resMean[i] / n;
					fileResStd << resStd[i] / n;
				}
				fileTimesMean << timesMean[i];
				fileTimesStd << timesStd[i];
			}
			fileResMean << endl;
			fileResStd << endl;
			fileTimesMean << endl;
			fileTimesStd << endl;
		}
	}

	fileResMean.close();
	fileResStd.close();
	fileTimesMean.close();
	fileTimesStd.close();

	cerr << "DONE" << endl;
}

/*
Reads a file in the following style:

First line is a comment starting with % symbol.
Second line is a comment starting with % symbol followed by m -- number of 
	edges, followed by n - number of vertices.
This is followed by m lines, each giving a pair of number between 1 and n.
This describes edges of a simple graph.

While reading the graph it is randomly parititoned into two roughly balanced 
sets of nodes and only those edges are kept that go between the two sets.
*/
Graph readGraphNetworkRepoStyle(ifstream &is, bool subSample = false, 
	int nSubSamp = 1000, bool duplicatingMethod = false) {
	char line[1024];
	char temp[128];
	int n = 0, m = 0;

	// Read the first useless comment line and discard
	is.getline(line, 1023);

	// Read the second comment line that contains number of edges and number
	// of nodes
	is.getline(line, 1023);
	sscanf_s(line, "%s %d %d", temp, 127, &m, &n);

	if (!duplicatingMethod) {
		vector<int> names(n, 0);
		for (int i = 0; i < n; i++) {
			names[i] = i;
		}
		random_shuffle(names.begin(), names.end());

		map<int, int> revNames;
		for (int i = 0; i < n; i++)
			revNames[names[i]] = i;

		if (subSample) n = nSubSamp;

		Graph res(n);
		res.setLeftSize(n / 2);


		for (int i = 0; i < m; i++) {
			is.getline(line, 1023);
			int from, to;
			sscanf_s(line, "%d %d", &from, &to);
			from--;
			to--;
			if (revNames[from] < n / 2 && revNames[to] >= n / 2 && 
				revNames[to] < n)
				res.addEdge(revNames[from], revNames[to]);
		}

		if (DEBUG_LEVEL > 1)
			res.printDebug();
		return res;
	}
	else {
		Graph res(n+n);
		res.setLeftSize(n);


		for (int i = 0; i < m; i++) {
			is.getline(line, 1023);
			int from, to;
			sscanf_s(line, "%d %d", &from, &to);
			from--;
			to--;
			res.addEdge(from, n+to);
		}

		if (DEBUG_LEVEL > 1)
			res.printDebug();
		return res;
	}
}

/*
Writes a table in latex of results. Column names correspond to experiment
names, row names correspond to algorithms.

vals has rowNames.size() number of rows and colNames.size() number of columns
*/
void writeTableToFile(vector<vector<double> > & vals, vector<string> & colNames,
	vector<string> & rowNames, ofstream& out, int precision = 2) {
	out << "\\begin{center}" << endl;
	out << "\\begin{tabular}{ |c||";
	for (int j = 0; j < (int)colNames.size(); j++) {
		out << "c|";
	}
	out << "}" << endl;
	out << "\\hline" << endl;
	out << "Algorithm";
	for (int j = 0; j < (int)colNames.size(); j++) {
		out << " &" << colNames[j];
	}
	out << "\\\\" << endl;
	out << "\\hline" << endl;
	out << "\\hline" << endl;

	for (int i = 0; i < (int)rowNames.size(); i++) {
		out << rowNames[i];
		for (int j = 0; j < (int)colNames.size(); j++) {
			out << " & " << std::fixed << std::setprecision(precision) << 
				vals[i][j];
		}
		out << "\\\\" << endl;
		out << "\\hline" << endl;
	}

	out << "\\end{tabular}" << endl;
	out << "\\end{center}" << endl;
}

/*
Return the transpose of the table
*/
vector<vector<double> > transpose(vector<vector<double> > inTable) {
	int n = inTable.size();
	int m = inTable[0].size();
	vector<vector<double> > outTable(m, vector<double>(n, 0));
	for (int i = 0; i < n; i++)
		for (int j = 0; j < m; j++)
			outTable[j][i] = inTable[i][j];
	return outTable;
}

/*
Run real life instances. The instances should be stored in folder 
"Real Graphs" which should be located in the same place as the executable.
*/
void experimentRealLife(bool duplicatingMethod = false, 
	bool competitive = true) {
	vector<string> fNames;
	vector<string> shortNames;
	vector<bool> subSample;
	int n = 2000;
	int rep = 100;

	
	string base = "RealWorld\\"; 
	fNames.push_back("socfb-Caltech36"); subSample.push_back(false); 
	shortNames.push_back("Caltech36");
	fNames.push_back("socfb-Reed98"); subSample.push_back(false); 
	shortNames.push_back("Reed98");
	fNames.push_back("bio-CE-GN"); subSample.push_back(false); 
	shortNames.push_back("CE-GN");
	fNames.push_back("bio-CE-PG"); subSample.push_back(false); 
	shortNames.push_back("CE-PG");
	fNames.push_back("econ-beause"); subSample.push_back(false); 
	shortNames.push_back("beause");
	fNames.push_back("econ-mbeaflw"); subSample.push_back(false); 
	shortNames.push_back("mbeaflw");
	
	ofstream fRes;
	if(!duplicatingMethod)
		fRes.open(base+"results.tex");
	else
		fRes.open(base+"results_dup.tex");
	
	vector<vector<double> > allRes;
	vector<vector<double> > allTimes;
	vector<string> allNames;

	for (int i = 0; i < (int)fNames.size(); i++) {
		vector<vector<double> > curRes;
		vector<vector<double> > curTimes;

		cerr << "Processing " << fNames[i] << endl;
		cerr << "Rep no:";
		for (int z = 0; z < rep; z++) {
			cerr << " " << (z + 1);
			ifstream file(base + fNames[i] + "\\" + fNames[i] + ".txt", 
				ifstream::in);
			Graph g = readGraphNetworkRepoStyle(file, subSample[i], n, 
				duplicatingMethod);
			file.close();

			vector<double> res;
			vector<double> times;
			vector<string> names;
			runIIDAllOnGraph(g, res, times, names);
			allNames = names;
			
			if (z == 0) {
				for (int j = 0; j < res.size(); j++) {
					curRes.push_back(vector<double>(rep, 0));
					curTimes.push_back(vector<double>(rep, 0));
				}
			}
			for (int j = 0; j < res.size(); j++) {
				curRes[j][z] = res[j];
				curTimes[j][z] = times[j];
			}
		}
		cerr << endl;
		vector<double> curMeanRes(curRes.size(), 0);
		vector<double> curStdRes(curRes.size(), 0);
		vector<double> curMeanTimes(curTimes.size(), 0);
		vector<double> curStdTimes(curTimes.size(), 0);
		
		for (int j = 0; j < curRes.size(); j++) {
			getMeanAndStd(curRes[j], curMeanRes[j], curStdRes[j]);
			getMeanAndStd(curTimes[j], curMeanTimes[j], curStdTimes[j]);
		}
		if (competitive) {
			for (int j = 0; j < (int)curRes.size(); j++) {
				curMeanRes[j] /= curMeanRes[(int)curRes.size() - 1];
				curStdRes[j] /= curMeanRes[(int)curRes.size() - 1];
			}
		}
		allRes.push_back(curMeanRes);
		allTimes.push_back(curMeanTimes);
		//allNames = names;
	}

	vector<vector<double> > trRes = transpose(allRes);
	writeTableToFile(trRes, shortNames, allNames, fRes);

	fRes.close();
}

/*
Run experiments on stand alone graphs
*/
void experimentStandAlone() {
	int rep = 100;
	int expno = 0;
	vector<double> curRes, curTimes;
	
	vector<string> graphNames;
	vector<string> shortGraphNames;

	graphNames.push_back("Upper Triangular"); shortGraphNames.push_back("UT");
	graphNames.push_back("Manshadi Hard"); shortGraphNames.push_back("MH");
	graphNames.push_back("Feldman Hard"); shortGraphNames.push_back("FH");
	graphNames.push_back("FewG"); shortGraphNames.push_back("FewG");
	graphNames.push_back("ManyG"); shortGraphNames.push_back("ManyG");
	graphNames.push_back("Rope"); shortGraphNames.push_back("Rope");
	graphNames.push_back("Hexa"); shortGraphNames.push_back("Hexa");
	graphNames.push_back("Zipf"); shortGraphNames.push_back("Zipf");

	string basestr = "StandAlone"; 
	ofstream fileResAbsMean, fileResAbsStd, fileTimesMean, fileTimesStd, 
		fileResCRMean, fileResCRStd;

	cerr << "RUNNING STAND ALONE EXPERIMENTS" << endl;
	cerr << "  the following type graphs are tested:" << endl;
	for (int i = 0; i < (int)graphNames.size(); i++) {
		cerr << "    " << graphNames[i] << endl;
	}

	vector<vector<double> > resAbsMean, resAbsStd, timesMean, timesStd, 
		resCRMean, resCRStd;
	vector<string> algoNames;

	string fnameResAbsMean = basestr + "ResAbsMean.tex";
	string fnameResAbsStd = basestr + "ResAbsStd.tex";
	string fnameResCRMean = basestr + "ResCRMean.tex";
	string fnameResCRStd = basestr + "ResCRStd.tex";
	string fnameTimesMean = basestr + "TimesMean.tex";
	string fnameTimesStd = basestr + "TimesStd.tex";

	cerr << "Running Upper Triangular" << endl;
	
	int nUT = 200; // replace by 200
	Graph gUT = triangular(nUT);

	cerr << "  params: " << " n = " << nUT << endl;

	vector<vector<double> > resUT;
	vector<vector<double> > timesUT;
	cerr << "  Rep no:";
	for (int i = 0; i < rep; i++) {
		cerr << " " << (i + 1);
		runIIDAllOnGraph(gUT, curRes, curTimes, algoNames);
		if (i == 0) {
			for (int j = 0; j < algoNames.size(); j++) {
				resUT.push_back(vector<double>());
				timesUT.push_back(vector<double>());
			}
		}
		for (int j = 0; j < algoNames.size(); j++) {
			resUT[j].push_back(curRes[j]);
			timesUT[j].push_back(curTimes[j]);
		}
	}
	cerr << endl;

	for (int i = 0; i < (int)algoNames.size(); i++) {
		resAbsMean.push_back(vector<double>(graphNames.size(), 0.0));
		resAbsStd.push_back(vector<double>(graphNames.size(), 0.0));
		resCRMean.push_back(vector<double>(graphNames.size(), 0.0));
		resCRStd.push_back(vector<double>(graphNames.size(), 0.0));
		timesMean.push_back(vector<double>(graphNames.size(), 0.0));
		timesStd.push_back(vector<double>(graphNames.size(), 0.0));
	}

	for (int i = 0; i < (int)algoNames.size(); i++) {
		getMeanAndStd(resUT[i], resAbsMean[i][expno], resAbsStd[i][expno]);
		getMeanAndStd(timesUT[i], timesMean[i][expno], timesStd[i][expno]);
	}

	for (int i = 0; i < (int)algoNames.size(); i++) {
		resCRMean[i][expno] = resAbsMean[i][expno] / 
			resAbsMean[(int)resAbsMean.size() - 1][expno];
		resCRStd[i][expno] = resAbsStd[i][expno] / 
			resAbsMean[(int)resAbsMean.size() - 1][expno];
	}
	expno++;


	cerr << "Running Manshadi Hard" << endl;

	int nMH = 300; // replace by 300
	Graph gMH = manshadiWorstCase(nMH);

	cerr << "  params: " << " n = " << nMH << endl;

	vector<vector<double> > resMH;
	vector<vector<double> > timesMH;
	cerr << "  Rep no:";
	for (int i = 0; i < rep; i++) {
		cerr << " " << (i + 1);
		runIIDAllOnGraph(gMH, curRes, curTimes, algoNames);
		if (i == 0) {
			for (int j = 0; j < algoNames.size(); j++) {
				resMH.push_back(vector<double>());
				timesMH.push_back(vector<double>());
			}
		}
		for (int j = 0; j < algoNames.size(); j++) {
			resMH[j].push_back(curRes[j]);
			timesMH[j].push_back(curTimes[j]);
		}
	}
	cerr << endl;

	for (int i = 0; i < (int)algoNames.size(); i++) {
		getMeanAndStd(resMH[i], resAbsMean[i][expno], resAbsStd[i][expno]);
		getMeanAndStd(timesMH[i], timesMean[i][expno], timesStd[i][expno]);
	}

	for (int i = 0; i < (int)algoNames.size(); i++) {
		resCRMean[i][expno] = resAbsMean[i][expno] / 
			resAbsMean[(int)resAbsMean.size() - 1][expno];
		resCRStd[i][expno] = resAbsStd[i][expno] / 
			resAbsMean[(int)resAbsMean.size() - 1][expno];
	}
	expno++;





	cerr << "Running Feldman Hard" << endl;

	int nFH = 300; // replace by 300
	Graph gFH = worstCaseFeldman(nFH);

	cerr << "  params: " << " n = " << nFH << endl;

	vector<vector<double> > resFH;
	vector<vector<double> > timesFH;
	cerr << "  Rep no:";
	for (int i = 0; i < rep; i++) {
		cerr << " " << (i + 1);
		runIIDAllOnGraph(gFH, curRes, curTimes, algoNames);
		if (i == 0) {
			for (int j = 0; j < algoNames.size(); j++) {
				resFH.push_back(vector<double>());
				timesFH.push_back(vector<double>());
			}
		}
		for (int j = 0; j < algoNames.size(); j++) {
			resFH[j].push_back(curRes[j]);
			timesFH[j].push_back(curTimes[j]);
		}
	}
	cerr << endl;

	for (int i = 0; i < (int)algoNames.size(); i++) {
		getMeanAndStd(resFH[i], resAbsMean[i][expno], resAbsStd[i][expno]);
		getMeanAndStd(timesFH[i], timesMean[i][expno], timesStd[i][expno]);
	}

	for (int i = 0; i < (int)algoNames.size(); i++) {
		resCRMean[i][expno] = resAbsMean[i][expno] / 
			resAbsMean[(int)resAbsMean.size() - 1][expno];
		resCRStd[i][expno] = resAbsStd[i][expno] / 
			resAbsMean[(int)resAbsMean.size() - 1][expno];
	}
	expno++;




	cerr << "Running FewG" << endl;

	int gFg = 32; // change to 32
	int szFg = 32; // change to 32
	int dFg = 5;

	cerr << "  params: " << " g = " << gFg << " sz = " << szFg << " d = " <<
		dFg << endl;

	vector<vector<double> > resFg;
	vector<vector<double> > timesFg;
	cerr << "  Rep no:";
	for (int i = 0; i < rep; i++) {
		cerr << " " << (i + 1);
		Graph graphFg = fewgAndManyg(gFg, szFg, dFg);
		runIIDAllOnGraph(graphFg, curRes, curTimes, algoNames);
		if (i == 0) {
			for (int j = 0; j < algoNames.size(); j++) {
				resFg.push_back(vector<double>());
				timesFg.push_back(vector<double>());
			}
		}
		for (int j = 0; j < algoNames.size(); j++) {
			resFg[j].push_back(curRes[j]);
			timesFg[j].push_back(curTimes[j]);
		}
	}
	cerr << endl;

	for (int i = 0; i < (int)algoNames.size(); i++) {
		getMeanAndStd(resFg[i], resAbsMean[i][expno], resAbsStd[i][expno]);
		getMeanAndStd(timesFg[i], timesMean[i][expno], timesStd[i][expno]);
	}

	for (int i = 0; i < (int)algoNames.size(); i++) {
		resCRMean[i][expno] = resAbsMean[i][expno] / 
			resAbsMean[(int)resAbsMean.size() - 1][expno];
		resCRStd[i][expno] = resAbsStd[i][expno] / 
			resAbsMean[(int)resAbsMean.size() - 1][expno];
	}
	expno++;


	cerr << "Running ManyG" << endl;

	int gMg = 256; // change to 256
	int szMg = 4; // change to 4
	int dMg = 5;

	cerr << "  params: " << " g = " << gMg << " sz = " << szMg << " d = " << 
		dMg << endl;

	vector<vector<double> > resMg;
	vector<vector<double> > timesMg;
	cerr << "  Rep no:";
	for (int i = 0; i < rep; i++) {
		cerr << " " << (i + 1);
		Graph graphFgMg2 = fewgAndManyg(gMg, szMg, dMg);
		runIIDAllOnGraph(graphFgMg2, curRes, curTimes, algoNames);
		if (i == 0) {
			for (int j = 0; j < algoNames.size(); j++) {
				resMg.push_back(vector<double>());
				timesMg.push_back(vector<double>());
			}
		}
		for (int j = 0; j < algoNames.size(); j++) {
			resMg[j].push_back(curRes[j]);
			timesMg[j].push_back(curTimes[j]);
		}
	}
	cerr << endl;

	for (int i = 0; i < (int)algoNames.size(); i++) {
		getMeanAndStd(resMg[i], resAbsMean[i][expno], resAbsStd[i][expno]);
		getMeanAndStd(timesMg[i], timesMean[i][expno], timesStd[i][expno]);
	}

	for (int i = 0; i < (int)algoNames.size(); i++) {
		resCRMean[i][expno] = resAbsMean[i][expno] / 
			resAbsMean[(int)resAbsMean.size() - 1][expno];
		resCRStd[i][expno] = resAbsStd[i][expno] / 
			resAbsMean[(int)resAbsMean.size() - 1][expno];
	}
	expno++;



	cerr << "Running Rope" << endl;

	int kRope = 166; // change to 166
	int dRope = 6; // change to 6

	cerr << "  params: " << " k = " << kRope << " d = " << dRope << endl;

	vector<vector<double> > resRope;
	vector<vector<double> > timesRope;
	cerr << "  Rep no:";
	for (int i = 0; i < rep; i++) {
		cerr << " " << (i + 1);
		Graph gRope = rope(kRope, dRope);
		runIIDAllOnGraph(gRope, curRes, curTimes, algoNames);
		if (i == 0) {
			for (int j = 0; j < algoNames.size(); j++) {
				resRope.push_back(vector<double>());
				timesRope.push_back(vector<double>());
			}
		}
		for (int j = 0; j < algoNames.size(); j++) {
			resRope[j].push_back(curRes[j]);
			timesRope[j].push_back(curTimes[j]);
		}
	}
	cerr << endl;

	for (int i = 0; i < (int)algoNames.size(); i++) {
		getMeanAndStd(resRope[i], resAbsMean[i][expno], resAbsStd[i][expno]);
		getMeanAndStd(timesRope[i], timesMean[i][expno], timesStd[i][expno]);
	}

	for (int i = 0; i < (int)algoNames.size(); i++) {
		resCRMean[i][expno] = resAbsMean[i][expno] / 
			resAbsMean[(int)resAbsMean.size() - 1][expno];
		resCRStd[i][expno] = resAbsStd[i][expno] / 
			resAbsMean[(int)resAbsMean.size() - 1][expno];
	}
	expno++;




	cerr << "Running Hexa" << endl;

	int bHexa = 32; // change to 32

	cerr << "  params: " << " b = " << bHexa << endl;

	vector<vector<double> > resHexa;
	vector<vector<double> > timesHexa;
	cerr << "  Rep no:";
	for (int i = 0; i < rep; i++) {
		cerr << " " << (i + 1);
		Graph gHexa = hexa(bHexa);
		runIIDAllOnGraph(gHexa, curRes, curTimes, algoNames);
		if (i == 0) {
			for (int j = 0; j < algoNames.size(); j++) {
				resHexa.push_back(vector<double>());
				timesHexa.push_back(vector<double>());
			}
		}
		for (int j = 0; j < algoNames.size(); j++) {
			resHexa[j].push_back(curRes[j]);
			timesHexa[j].push_back(curTimes[j]);
		}
	}
	cerr << endl;

	for (int i = 0; i < (int)algoNames.size(); i++) {
		getMeanAndStd(resHexa[i], resAbsMean[i][expno], resAbsStd[i][expno]);
		getMeanAndStd(timesHexa[i], timesMean[i][expno], timesStd[i][expno]);
	}

	for (int i = 0; i < (int)algoNames.size(); i++) {
		resCRMean[i][expno] = resAbsMean[i][expno] / 
			resAbsMean[(int)resAbsMean.size() - 1][expno];
		resCRStd[i][expno] = resAbsStd[i][expno] / 
			resAbsMean[(int)resAbsMean.size() - 1][expno];
	}
	expno++;





	cerr << "Running Zipf" << endl;

	int nZipf = 1000; // change to 1000
	int dZipf = 6; // change to 6

	cerr << "  params: " << " n = " << nZipf << " d = " << dZipf << endl;

	vector<vector<double> > resZipf;
	vector<vector<double> > timesZipf;
	cerr << "  Rep no:";
	for (int i = 0; i < rep; i++) {
		cerr << " " << (i + 1);
		Graph gZipf = zipf(nZipf, dZipf);
		runIIDAllOnGraph(gZipf, curRes, curTimes, algoNames);
		if (i == 0) {
			for (int j = 0; j < algoNames.size(); j++) {
				resZipf.push_back(vector<double>());
				timesZipf.push_back(vector<double>());
			}
		}
		for (int j = 0; j < algoNames.size(); j++) {
			resZipf[j].push_back(curRes[j]);
			timesZipf[j].push_back(curTimes[j]);
		}
	}
	cerr << endl;

	for (int i = 0; i < (int)algoNames.size(); i++) {
		getMeanAndStd(resZipf[i], resAbsMean[i][expno], resAbsStd[i][expno]);
		getMeanAndStd(timesZipf[i], timesMean[i][expno], timesStd[i][expno]);
	}

	for (int i = 0; i < (int)algoNames.size(); i++) {
		resCRMean[i][expno] = resAbsMean[i][expno] / 
			resAbsMean[(int)resAbsMean.size() - 1][expno];
		resCRStd[i][expno] = resAbsStd[i][expno] / 
			resAbsMean[(int)resAbsMean.size() - 1][expno];
	}
	expno++;






	fileResAbsMean.open(fnameResAbsMean);
	fileResAbsStd.open(fnameResAbsStd);
	fileResCRMean.open(fnameResCRMean);
	fileResCRStd.open(fnameResCRStd);
	fileTimesMean.open(fnameTimesMean);
	fileTimesStd.open(fnameTimesStd);

	writeTableToFile(resAbsMean, shortGraphNames, algoNames, fileResAbsMean, 0);
	writeTableToFile(resAbsStd, shortGraphNames, algoNames, fileResAbsStd, 0);
	writeTableToFile(resCRMean, shortGraphNames, algoNames, fileResCRMean, 2);
	writeTableToFile(resCRStd, shortGraphNames, algoNames, fileResCRStd, 2);
	writeTableToFile(timesMean, shortGraphNames, algoNames, fileTimesMean, 2);
	writeTableToFile(timesStd, shortGraphNames, algoNames, fileTimesStd, 2);

	fileResAbsMean.close();
	fileResAbsStd.close();
	fileResCRMean.close();
	fileResCRStd.close();
	fileTimesMean.close();
	fileTimesStd.close();
}


void printHeader() {
	cout <<
		"BipartiteMatching  Copyright(C) 2018  Denis Pankratov\n\n"
		"This program comes with ABSOLUTELY NO WARRANTY; \n"
		"This is free software, and you are welcome to redistribute it\n"
		"under certain conditions; see the accompanying documentation for "
		"details.\n\n";
}

// This is where the magic happens.
int main()
{
	srand((unsigned int)time(NULL));
	
	printHeader();

	cout << "Which experiment would you like to run?\n"
		"(1) ErdosRenyi\n"
		"(2) Preferential Attachment\n"
		"(3) Regular\n"
		"(4) MolloyReed\n"
		"(5) StandAlone\n"
		"(6) RealWorld\n"
		"Type your choice (integer): ";
	int experiment;
	cin >> experiment;
	switch (experiment) {
	case 1: experimentErdosRenyi();
		break;
	case 2: experimentPreferentialAttachmentBiGraph();
		break;
	case 3: experimentRegular();
		break;
	case 4: experimentPowerLawMolloyReed();
		break;
	case 5: experimentStandAlone();
		break;
	case 6: experimentRealLife();
		break;
	default:
		cout << "Invalid input!" << endl;
		break;
	}

	cerr << "DONE!" << endl;
    return 0;
}

